﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using SharpMap;
using SharpMap.Geometries;
using SharpMap.Layers;
using SharpMap.Providers;
using SharpMap.Rendering;
using SharpMap.Styles;
using SharpMap.Styles.Thematics;

namespace SilverlightRendering
{
    public class MapRenderer : IRenderer
    {
        private Canvas canvas;
        private Dictionary<string, Canvas> tileCanvas = new Dictionary<string, Canvas>();
        private readonly TileRenderer tileRenderer = new TileRenderer();
        private static readonly IDictionary<ILayer, IDictionary<IFeature, IStyle>> StyleCache = new Dictionary<ILayer, IDictionary<IFeature, IStyle>>();

        public Canvas Canvas
        {
            get { return canvas; }
        }

        public MapRenderer()
        {
            canvas = new Canvas();
        }

        public void Render(IView view, Map map)
        {
            //TODO: remove try catch, fix exception when removing layer
            try
            {
                canvas.Children.Clear();
                canvas = new Canvas();

                //For each layer create a canvas and add to the dictionary (Used to create the possibility to set transparancy for different tile layers)
                foreach (var layer in map.Layers)
                {
                    if (tileCanvas.ContainsKey(layer.LayerName))
                        continue;

                    var layerCanvas = new Canvas();
                    tileCanvas.Add(layer.LayerName, layerCanvas);
                }

                foreach (var key in tileCanvas.Keys)
                {
                    var layer = map.Layers.FindLayer(key) as ILayer;

                    if (layer == null)
                    {
                        tileCanvas.Remove(key);
                        continue;
                    }

                    if (!layer.Enabled)
                        continue;

                    var layerCanvas = tileCanvas[key];
                    layerCanvas.Opacity = layer.Opacity;
                    canvas.Children.Add(layerCanvas);
                }

                foreach (var layer in map.Layers)
                {
                    if (layer.Enabled &&
                        layer.MinVisible <= view.Resolution &&
                        layer.MaxVisible >= view.Resolution)
                    {
                        RenderLayer(view, layer);
                    }
                }
                canvas.Arrange(new System.Windows.Rect(0, 0, view.Width, view.Height));
            }
            catch (Exception)
            {
                return;
            }
        }

        private void RenderLayer(IView view, ILayer layer)
        {
            //Ideally I would like a solution where all rendering can be done through a single interface
            //whithout the type check below.
            if (layer is LabelLayer)
            {
                var labelLayer = layer as LabelLayer;
                if (labelLayer.UseLabelStacking)
                    LabelRenderer.RenderStackedLabelLayer(canvas, view, labelLayer);
                else
                    LabelRenderer.RenderLabelLayer(canvas, view, labelLayer);
            }
            else if (layer is ITileLayer)
            {
                var tileLayer = (ITileLayer)layer;
                tileRenderer.Render(tileCanvas[layer.LayerName], tileLayer.Schema, view, tileLayer.MemoryCache);
            }
            else
            {
                RenderVectorLayer(canvas, view, layer);
            }
        }

          //Changed: TIME
        private static void RenderVectorLayer(Canvas canvas, IView view, ILayer layer)
        {
            var features = layer.GetFeaturesInView(view.Extent, view.Resolution);

            foreach (var feature in features)
            {
                if (feature.Style == null)
                    continue;

                var style = feature.Style;
                if (style is IThemeStyle) style = (style as IThemeStyle).GetStyle(feature);
                if ((style == null) || (style.Enabled == false) || (style.MinVisible > view.Resolution) ||
                    (style.MaxVisible < view.Resolution)) continue;

                if (feature.Geometry is Point)
                    canvas.Children.Add(GeometryRenderer.RenderPoint(feature.Geometry as Point, feature.Style, view));
                else if (feature.Geometry is MultiPoint)
                    canvas.Children.Add(GeometryRenderer.RenderMultiPoint(feature.Geometry as MultiPoint, feature.Style,
                                                                          view));
                else if (feature.Geometry is LineString)
                    canvas.Children.Add(GeometryRenderer.RenderLineString(feature.Geometry as LineString, feature.Style,
                                                                          view));
                else if (feature.Geometry is MultiLineString)
                    canvas.Children.Add(GeometryRenderer.RenderMultiLineString(feature.Geometry as MultiLineString,
                                                                               feature.Style, view));
                else if (feature.Geometry is Polygon)
                    canvas.Children.Add(GeometryRenderer.RenderPolygon(feature.Geometry as Polygon, feature.Style, view));
                else if (feature.Geometry is MultiPolygon)
                    canvas.Children.Add(GeometryRenderer.RenderMultiPolygon(feature.Geometry as MultiPolygon,
                                                                            feature.Style, view));
                else if (feature.Geometry is IRaster)
                    canvas.Children.Add(GeometryRenderer.RenderRaster(feature.Geometry as IRaster, feature.Style, view));
            }

        }

        private static IStyle GetCache(ILayer layer, IFeature feature)
        {
            if (StyleCache.ContainsKey(layer))
                if (StyleCache[layer].ContainsKey(feature))
                    return StyleCache[layer][feature];
            return null;
        }

        private static void SetCache(ILayer layer, IFeature feature, IStyle style)
        {
            if (!StyleCache.ContainsKey(layer))
                StyleCache[layer] = new Dictionary<IFeature, IStyle>();
            StyleCache[layer][feature] = style;
        }

        public Stream ToBitmapStream(double width, double height)
        {
            canvas.Arrange(new System.Windows.Rect(0, 0, width, height));

#if !SILVERLIGHT
        
            var renderTargetBitmap = new RenderTargetBitmap((int)width, (int)height, 96, 96, new PixelFormat());
            renderTargetBitmap.Render(canvas);
            var bitmap = new PngBitmapEncoder();
            bitmap.Frames.Add(BitmapFrame.Create(renderTargetBitmap));
            var bitmapStream = new MemoryStream();
            bitmap.Save(bitmapStream);
            
#else

            var writeableBitmap = new WriteableBitmap((int)width, (int)height);
            writeableBitmap.Render(canvas, null);
            var bitmapStream = Utilities.ConverToBitmapStream(writeableBitmap);

#endif

            return bitmapStream;
        }
    }
}
